package com.atguigu.exam.service.impl;


import com.atguigu.exam.entity.Category;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.mapper.CategoryMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.CategoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.Format;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.LongStream;


@Slf4j
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Autowired
    private QuestionMapper questionMapper;

    /**
     * 获取所有题目分类列表，包含每个分类下的题目数量统计
     * @return
     */
    @Override
    public List<Category> getCategoryList() {
        //查询所有分类信息
        List<Category> categoryList = list();
        //完成count填充
        fillCategoryCount(categoryList);
        //返回结果
        return categoryList;
    }

    /**
     * 获取题目分类的树形层级结构
     * @return
     */
    @Override
    public List<Category> getCategoryTreeList() {
        //查询所有分类信息
        List<Category> categoryList = list();
        //完成count填充
        fillCategoryCount(categoryList);
        //给所有分类根据parent_id进行分组，这里需要通过筛选来进行实现
        // 因为这里需要记录每一个类别对应的父类别，结果不一定是一个值，所以不能通过sql语句实现，不是统计数量
        Map<Long, List<Category>> parentIdMap = categoryList.stream().collect(Collectors.groupingBy(Category::getParentId));
        //筛选原来分类集合中 parent_id = 0的，也就是一级标题
        List<Category> categoryListResult = categoryList.stream().filter(c -> c.getParentId() == 0).collect(Collectors.toList());
        /*
            对筛选后的集合进行循环遍历，进行以下操作
                1、map给子分类集合赋值 child
                2、父分类count = 自己的count + 所有子分类的count
            思路：
            categoryListResult存储的是所有的一级标题，给一级标题的子标题id属性进行赋值
            parentIdMap 存储的是根据parent_id分组之后的每一个集合，所以这里只需要直接获取id即可
         */
        categoryListResult.forEach(category -> {
        //1、子分类集合赋值,这里因为之后再查询子分类的count的时候还需要用到子集合，所以我们选择将获取子分类的步骤提取出来
            List<Category> childCategoryList = parentIdMap.getOrDefault(category.getId(), new ArrayList<>());
            //需要在查询的时候，根据优先级进行排序
            childCategoryList.sort(Comparator.comparingInt(Category::getSort));
            //set方法设置children属性
            category.setChildren(childCategoryList);
        //2、父分类count = 自己的count + 所有子分类的count
            //获取所有子分类的count
            long ChildCount = childCategoryList.stream().mapToLong(Category::getCount).sum();
            category.setCount(category.getCount() + ChildCount);
        });
        //返回集合，筛选+循环赋值

        return categoryListResult;
    }

    /**
     * 给与分类信息进行count填充
     * 1. 判断分类集合是否为empty [对一个集合 对一个数据要进行大量逻辑代码之前，尽量先判断！否则出现无用功！！]
     * 2. 查询所有分类和分类对应的题目数量 【mapper方法】
     * 3. 进行分类数据和count转化，map<categoryId,count>
     * 4. 给集合每个分类，查询count,并赋值即可
     * @param categoryList
     */
    public void fillCategoryCount(List<Category> categoryList) {
        //1. 判断分类集合是否为empty
        if (categoryList == null || categoryList.isEmpty()) {
            throw new RuntimeException("查询分类集合不存在");
        }
        //2. 查询所有分类和分类对应的题目数量
        //通过调用mapper方法中方法来统计分组的题目id和对应每一组的题目数量
        List<Map<Long, Long>> mapList = questionMapper.selectCategoryCount();
        //将查询的结果，转换成一个map的格式，key是题目id，value是对应的题目数量
        Map<Long, Long> result = mapList.stream().collect(Collectors.toMap(q -> q.get("category_id"), q -> q.get("ct")));
        //Java代码下循环一次，将list中填充count
        //思路是get，set方法，填充categoryList的count属性，填充内容是从map集合中找，根据key为分组的id进行查找
        //getOrDefault()方法两个参数，一个是要查询的key的值，第二个值是如果未查询到赋的默认值
        categoryList.forEach(category -> {
            category.setCount(result.getOrDefault(category.getId(),0L));
        });
    }

    /**
     * 添加分类
     * @param category 分类对象
     * @return 操作结果
     */
    @Override
    public void saveCategory(Category category) {
        //先对传入category进行判断，如果存在，那么就不可以添加分类
        //判断，如果在同一个一级标题(也就是父id相同)，同时名字相同，那么就重复
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
        categoryLambdaQueryWrapper.eq(Category::getParentId,category.getParentId());
        categoryLambdaQueryWrapper.eq(Category::getName,category.getName());
        //对于当前serviceImpl对应的mapper，可以不使用@Autowired注解进行注入，而是通过getBaseMapper()方法获取
        boolean exists = getBaseMapper().exists(categoryLambdaQueryWrapper);
        //判断成立说明已存在，抛出异常
        if(exists){
            Category parent = getById(category.getParentId());
            throw new RuntimeException("在%s父分类下，已经存在名为：%s的子分类，本次添加失败！".formatted(parent.getName(),category.getName()));
        }
        //执行保存
        save(category);
    }

    /**
     * 更新分类
     * @param category 分类对象
     * @return 操作结果
     */
    @Override
    public void updateCategory(Category category) {
        //先对传入category进行判断
        //判断，如果在同一个一级标题(也就是父id相同)，id不同，同时名字相同，那么就重复，抛出异常
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
        categoryLambdaQueryWrapper.eq(Category::getParentId,category.getParentId());
        categoryLambdaQueryWrapper.ne(Category::getId,category.getId());
        categoryLambdaQueryWrapper.eq(Category::getName,category.getName());
        //对条件进行判断
        boolean exists = getBaseMapper().exists(categoryLambdaQueryWrapper);
        if(exists){
            Category parent = getById(category.getParentId());
            throw new RuntimeException("在%s父分类下，已经存在名为：%s的子分类，本次更新失败！".formatted(parent.getName(),category.getName()));
        }
        updateById(category);
    }

    /**
     * 删除分类
     * @param id 分类ID
     * @return 操作结果
     */
    @Override
    public void removeCategory(Long id) {
        //思路：首先判断不属于一级分类，也就是parent_id=0
        Category category = getById(id);
        if(category.getParentId() == 0){
            throw new RuntimeException("不能删除一级标题！");
        }
        //再判断是否有子分类，如果有子分类，也不应该删除
        // 这里的话就是指，如果这个分类下有对应的题目，题目数量不为0，那么无法删除
        LambdaQueryWrapper<Question> categoryLambdaQueryWrapper = new LambdaQueryWrapper<Question>();
        categoryLambdaQueryWrapper.eq(Question::getCategoryId,id);
        //执行条件，所得结果为在题目表中和类别id对应的题目的数量
        Long count = questionMapper.selectCount(categoryLambdaQueryWrapper);
        //如果大于0，说明存在关联的题目，那么这个类别就无法删除
        if(count>0){
            throw new RuntimeException("当前的:%s分类，关联了%s道题目,无法删除！".formatted(category.getName(),count));
        }
        //删除
        removeById(id);
    }
}